#include <common.hpp>
#include "OpenGLTools.hpp"
#include "../Resource/Shader/ShaderSpecify.hpp"
#include <Utility/Log.hpp>

namespace zzz{
GLfloat GLPointSize::old=1;
GLfloat GLLineWidth::old=1;
Vector<2, GLint> GLPolygonMode::old(GL_FILL,GL_FILL);
Color<GLfloat> GLClearColor::old(1,1,1,1);
GLfloat GLClearDepth::old=1.0f;
GLint GLDrawBuffer::old=GL_BACK;
GLint GLReadBuffer::old=GL_BACK;
Vector<4,GLint> GLViewport::old;
Vector<4,GLboolean> GLColorMask::old;
Vector<4,double> GLRasterPos::old;
GLint GLBindTexture1D::old;
GLint GLBindTexture2D::old;
GLint GLBindTexture3D::old;
GLint GLBindTextureCube::old;
GLint GLBindRenderBuffer::old;
GLint GLBindFrameBuffer::old;
GLint GLActiveTexture::old;

bool InitGLEW()
{
  // May be run more than once, for multiple renderer.

  GLenum err = glewInit();

  if (GLEW_OK != err)
  {
    ZLOG(ZERROR) << "Error:" << glewGetErrorString(err) << endl;
//    GLEWinited = false;
    return false;
  }

  ZLOG(ZVERBOSE) << "OpenGL Vendor: " << (char*) glGetString(GL_VENDOR) << "\n";
  ZLOG(ZVERBOSE) << "OpenGL Renderer: " << (char*) glGetString(GL_RENDERER) << "\n";
  ZLOG(ZVERBOSE) << "OpenGL Version: " << (char*) glGetString(GL_VERSION) << "\n\n";

  CheckGLVersion();
  if (CheckGLSL())
    MakeShaders();

  return true;
}

void CheckGLVersion()
{
  if (GLEW_VERSION_4_0)
    ZLOG(ZVERBOSE) << "OpenGL 4.0 is available!" << endl;
  else if (GLEW_VERSION_3_3)
    ZLOG(ZVERBOSE) << "OpenGL 3.3 is available!" << endl;
  else if (GLEW_VERSION_3_2)
    ZLOG(ZVERBOSE) << "OpenGL 3.2 is available!" << endl;
  else if (GLEW_VERSION_3_1)
    ZLOG(ZVERBOSE) << "OpenGL 3.1 is available!" << endl;
  else if (GLEW_VERSION_3_0)
    ZLOG(ZVERBOSE) << "OpenGL 3.0 is available!" << endl;
  else if (GLEW_VERSION_2_1)
    ZLOG(ZVERBOSE) << "OpenGL 2.1 is available!" << endl;
  else if (GLEW_VERSION_2_0)
    ZLOG(ZVERBOSE) << "OpenGL 2.0 is available!" << endl;
  else if (GLEW_VERSION_1_5) 
    ZLOG(ZVERBOSE) << "OpenGL 1.5 core functions are available\n!!!!!!!!Graphics Card Driver may not be installed correctly!!!!!!!!" << endl;
  else if (GLEW_VERSION_1_4)
    ZLOG(ZVERBOSE) << "OpenGL 1.4 core functions are available\n!!!!!!!!Graphics Card Driver may not be installed correctly!!!!!!!!" << endl;
  else if (GLEW_VERSION_1_3)
    ZLOG(ZVERBOSE) << "OpenGL 1.3 core functions are available\n!!!!!!!!Graphics Card Driver may not be installed correctly!!!!!!!!" << endl;
  else if (GLEW_VERSION_1_2)
    ZLOG(ZVERBOSE) << "OpenGL 1.2 core functions are available\n!!!!!!!!Graphics Card Driver may not be installed correctly!!!!!!!!" << endl;
}

bool CheckGLSL()
{
  static bool GLSL=false;
  if (GLSL==true) return true;
  GLSL=true;

  if (!CheckSupport("GL_ARB_fragment_shader"))
    GLSL=false;

  if (!CheckSupport("GL_ARB_vertex_shader"))
    GLSL=false;
  CheckSupport("GL_ARB_geometry_shader4");

  if (!CheckSupport("GL_ARB_shader_objects"))
    GLSL=false;

  if (GLSL)
    ZLOG(ZVERBOSE) << "GLSL is available!\n";
  else
    ZLOG(ZVERBOSE) << "GLSL is NOT available!\n";
  return GLSL;

}

bool CheckSupport(const string &ext)
{
  if (glewIsSupported(ext.c_str())==GL_TRUE) 
  {
    ZLOGI<<ext<<" is supported!\n";
    return true;
  }
  else
  {
    ZLOGI<<ext<<" is NOT supported\n";
    return false;
  }
}

int CheckGLError(char *file, int line)
{
  if (Context::current_context_==NULL) return 0;
  GLenum glErr;
  int retCode = 0;

  glErr = glGetError();
  while (glErr != GL_NO_ERROR)
  {
    ZLOG(ZERROR) << "GL Error #" << glErr << "(" ;
    const GLubyte *x=gluErrorString(glErr);
    if (x != NULL)
      ZLOG(ZERROR) << x;
    ZLOG(ZERROR) << ")  in File " << file << " at line: " << line << endl;
    retCode = 1;
    glErr = glGetError();
  }
  return retCode;
}


////////////////////////////////////////////////////////////

OpenGLProjector::OpenGLProjector()
{
  Refresh();
}

void OpenGLProjector::Refresh()
{
  glGetIntegerv(GL_VIEWPORT, viewport_);
  glGetDoublev(GL_MODELVIEW_MATRIX, modelview_);
  glGetDoublev(GL_PROJECTION_MATRIX, projection_);
}

zzz::Vector3d OpenGLProjector::UnProject(double winx, double winy, double winz)
{
  Vector3d obj;
  gluUnProject(winx, winy, winz, modelview_, projection_, viewport_, &obj[0], &obj[1], &obj[2]);
  return obj;
}

zzz::Vector3d OpenGLProjector::Project(double objx, double objy, double objz)
{
  Vector3d win;
  gluProject(objx, objy, objz, modelview_, projection_, viewport_, &win[0], &win[1], &win[2]);
  return win;

}

}